use std::collections::HashMap;

use dbus;
use dbus::arg;
use dbus::arg::*;
use dbus::blocking::stdintf::org_freedesktop_dbus::*;
use dbus::blocking::BlockingSender;
use dbus::blocking::Proxy;
use dbus::tree;

use crate::consts::DBUS_BLUEZ_IFACE_ADVERTISINGMANAGER;
use crate::DBusRemoteObject;
use crate::Result;

pub trait OrgBluezLEAdvertisingManager1 {
    fn register_advertisement(
        &self,
        advertisement: dbus::Path,
        options: ::std::collections::HashMap<&str, arg::Variant<Box<dyn arg::RefArg>>>,
    ) -> Result<()>;
    fn unregister_advertisement(&self, service: dbus::Path) -> Result<()>;
}

pub struct OrgBluezLEAdvertisingManager<'a, C> {
    proxy: Proxy<'a, C>,
}

impl<'a, C> OrgBluezLEAdvertisingManager<'a, C> {
    pub fn new(proxy: Proxy<'a, C>) -> Self {
        Self { proxy }
    }
}

impl<'a, T, C> DBusRemoteObject<'a, T, C> for OrgBluezLEAdvertisingManager<'a, C>
where
    T: BlockingSender,
    C: std::ops::Deref<Target = T>,
{
    fn proxy(&self) -> &Proxy<'a, C> {
        &self.proxy
    }

    fn interface(&self) -> &str {
        DBUS_BLUEZ_IFACE_ADVERTISINGMANAGER
    }
}

impl<'a, T, C> OrgBluezLEAdvertisingManager1 for OrgBluezLEAdvertisingManager<'a, C>
where
    T: BlockingSender,
    C: std::ops::Deref<Target = T>,
{
    fn register_advertisement(
        &self,
        advertisement: dbus::Path,
        options: ::std::collections::HashMap<&str, arg::Variant<Box<dyn arg::RefArg>>>,
    ) -> Result<()> {
        self.method_call("RegisterAdvertisement", (advertisement, options))
    }

    fn unregister_advertisement(&self, service: dbus::Path) -> Result<()> {
        self.method_call("UnregisterAdvertisement", (service,))
    }
}

pub trait OrgBluezLEAdvertisement1 {
    fn release();

    /// Type of advertising packet, "broadcast" or "peripheral"
    fn r#type() -> Result<String>;

    fn set_type() -> Result<()>;

    fn service_uuids() -> Result<Vec<String>>;

    fn set_service_uuids(uuids: Vec<String>);

    fn manufacturer_data() -> Result<HashMap<String, arg::Variant<Box<dyn arg::RefArg + 'static>>>>;

    fn set_manufacturer_data() -> Result<()>;

    fn solicit_uuids() -> Result<Vec<String>>;

    fn set_solicit_uuids() -> Result<()>;

    fn service_data() -> Result<()>;

    fn set_service_data() -> Result<()>;
}
